﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using Artisteer_Project_Converter.VisualStudioApi;

namespace Artisteer_Project_Converter.ArtisteerProjectApi
{
    public class ArtProject
    {
        private string basePath = "";
        private string cssPrefix = "";
        private string version = "";

        /// <summary>
        /// Select an Artisteer project
        /// </summary>
        public ArtProject()
        {
            using (FolderBrowserDialog dialog = new FolderBrowserDialog())
            {
                dialog.Description = Artisteer_Project_Converter.Resources.Title_SelectArtisteerProjectFolder;

                if (dialog.ShowDialog() != DialogResult.OK)
                    throw new Exception(Artisteer_Project_Converter.Resources.Exception_AbortedByUser);

                basePath = dialog.SelectedPath;
            }

            analyze();
        }

        /// <summary>
        /// Open an Artisteer project
        /// </summary>
        /// <param name="path"></param>
        public ArtProject(string path)
        {
            this.basePath = path;
            analyze();
        }

        /// <summary>
        /// Analyze Artisteer project
        /// </summary>
        private void analyze()
        {
            DirectoryInfo baseDir = new DirectoryInfo(basePath);

            // Check basic folder structure
            if (baseDir.GetFiles("DefaultMenu.ascx").Length == 0 ||
                baseDir.GetFiles("DefaultVerticalMenu.ascx").Length == 0 ||
                baseDir.GetFiles("script.js").Length == 0 || 
                baseDir.GetFiles("style.css").Length == 0 ||
                baseDir.GetFiles("web.config").Length == 0 ||
                baseDir.GetDirectories("App_Code").Length == 0 ||
                baseDir.GetDirectories("design").Length == 0 ||
                baseDir.GetDirectories("images").Length == 0)
                throw new Exception(Artisteer_Project_Converter.Resources.Exception_InvalidProjectFolder);

            // Read stylesheet
            string content = readSourceFromFile(System.IO.Path.Combine(basePath, "style.css"));
            Match match;

            // Determine Artisteer release
            match = Regex.Match(content, "(?<=^(" + Regex.Escape("/* Generated with Artisteer version ") + "|" +
                                                    Regex.Escape("/* Created by Artisteer v") + "))[0-9.]*", 
                                      RegexOptions.Multiline | RegexOptions.IgnoreCase);
            if (!match.Success)
                throw new Exception(Artisteer_Project_Converter.Resources.Exception_InvalidProjectFolder);
            else
                version = match.Value;

            // Determine Artisteer CSS prefix
            match = Regex.Match(content, "(?<=^#).*?(?=main\\s*$)", RegexOptions.Multiline);
            if (!match.Success)
                throw new Exception(Artisteer_Project_Converter.Resources.Exception_InvalidProjectFolder);
            else
                cssPrefix = match.Value;
        }

        /// <summary>
        /// Convert project
        /// </summary>
        /// <param name="vsProject">Visual Studio project</param>
        /// <param name="targetPath">Location of target folder</param>
        public void Convert(VsProject vsProject, string targetPath)
        {
            convertFolder(vsProject, BasePath, targetPath);
        }

        /// <summary>
        /// Convert project folder
        /// </summary>
        /// <param name="vsProject">Visual Studio project</param>
        /// <param name="sourcePath">Location of source folder</param>
        /// <param name="targetPath">Location of target folder</param>
        private void convertFolder(VsProject vsProject, string sourcePath, string targetPath)
        {
            // Process files
            foreach (string file in Directory.GetFiles(sourcePath))
            {
                string fileName = System.IO.Path.GetFileName(file);
                string fileNameLower = fileName.ToLower();
                string targetFile = System.IO.Path.Combine(targetPath, System.IO.Path.GetFileName(file));

                switch (System.IO.Path.GetExtension(file).ToLower())
                {
                    case ".ascx":
                    case ".aspx":
                    case ".master":
                        Console.WriteLine(string.Format(Artisteer_Project_Converter.Resources.Message_ConvertingFile, fileName));
                        convertAspxFile(vsProject, file, targetPath);
                        break;

                    case ".artx":
                    case ".css":
                    case ".gif":
                    case ".ico":
                    case ".jpg":
                    case ".js":
                    case ".png":
                        Console.WriteLine(string.Format(Artisteer_Project_Converter.Resources.Message_CopyingFile, fileName));
                        File.Copy(file, targetFile);
                        vsProject.AddFile(targetFile);
                        break;

                    case ".cs":
                    case ".vb":
                        if (!fileNameLower.EndsWith(".aspx.cs") &&
                            !fileNameLower.EndsWith(".aspx.vb") &&
                            !fileNameLower.EndsWith(".aspx.designer.cs") &&
                            !fileNameLower.EndsWith(".aspx.designer.vb") &&
                            !fileNameLower.EndsWith(".ascx.cs") &&
                            !fileNameLower.EndsWith(".ascx.vb") &&
                            !fileNameLower.EndsWith(".ascx.designer.cs") &&
                            !fileNameLower.EndsWith(".ascx.designer.vb") &&
                            !fileNameLower.EndsWith(".master.cs") &&
                            !fileNameLower.EndsWith(".master.vb") &&
                            !fileNameLower.EndsWith(".master.designer.cs") &&
                            !fileNameLower.EndsWith(".master.designer.vb"))
                        {
                            Console.WriteLine(string.Format(Artisteer_Project_Converter.Resources.Message_CopyingFile, fileName));
                            File.Copy(file, targetFile);
                            vsProject.AddFile(targetFile);
                        }

                        break;
                }
            }

            // Process sub-folders
            foreach (string subFolder in Directory.GetDirectories(sourcePath))
            {
                string folderName = System.IO.Path.GetFileName(subFolder);

                if (folderName.ToLower() == "app_code") folderName = "artisteer";

                Console.WriteLine(string.Format(Artisteer_Project_Converter.Resources.Message_CreatingFolder, folderName));

                vsProject.addFolder(folderName);
                convertFolder(vsProject, subFolder, System.IO.Path.Combine(targetPath, folderName));
            }
        }

        /// <summary>
        /// Convert Artisteer *.aspx/*.ascx file
        /// </summary>
        /// <param name="project">Visual Studio project</param>
        /// <param name="sourcePath">Path to source file</param>
        /// <param name="targetPath">Path to store target file</param>
        private void convertAspxFile(VsProject project, string sourcePath, string targetPath)
        {
            FileInfo sourceFileInfo = new FileInfo(sourcePath);

            string fileName = sourceFileInfo.Name;
            string content;

            // Convert *.aspx file
            content = readSourceFromFile(sourceFileInfo.FullName);

            content = Regex.Replace(content, "CodeFile=\"",
                                             "CodeBehind=\"",
                                             RegexOptions.Multiline);

            content = Regex.Replace(content, Regex.Escape("<%@ Import Namespace=\"Artisteer\" %>"),
                                             "<%@ Register Assembly=\"" + project.Name + "\" TagPrefix=\"artisteer\" Namespace=\"Artisteer\" %>",
                                             RegexOptions.Multiline);

            // Conversion of menus
            if (fileName == "DefaultMenu.ascx")
            {  
                content = content.Substring(0, content.IndexOf("\n") + 1) +
                          "<%@ Register Assembly=\"" + project.Name + "\" TagPrefix=\"artisteer\" Namespace=\"Artisteer\" %>\n" +
                          "<%@ Register TagPrefix=\"artisteer\" Namespace=\"Artisteer\" %>\n" + 
                          content.Substring(content.IndexOf("\n") + 1);
                
                content = Regex.Replace(content, "<ul\\s.*</ul>",
                                                 "<artisteer:Menu ID=\"DefaultHMenu\" DataSourceID=\"DefaultHMenuDatasource\" CssClass=\"" + HorizontalMenuCssStyle + "\" runat=\"server\" />",
                                                 RegexOptions.Singleline)
                        + "<asp:SiteMapDataSource ID=\"DefaultHMenuDatasource\" runat=\"server\" />";
            }

            if (fileName == "DefaultVerticalMenu.ascx")
            {
                content = content.Substring(0, content.IndexOf("\n") + 1) +
                          "<%@ Register Assembly=\"" + project.Name + "\" TagPrefix=\"artisteer\" Namespace=\"Artisteer\" %>\n" +
                          "<%@ Register TagPrefix=\"artisteer\" Namespace=\"Artisteer\" %>\n" + 
                          content.Substring(content.IndexOf("\n") + 1);
                
                content = Regex.Replace(content, Regex.Escape("<ul") + ".*" + Regex.Escape("</ul>"),
                                                 "<artisteer:Menu ID=\"DefaultVMenu\" DataSourceID=\"DefaultVMenuDatasource\" CssClass=\"" + VerticalMenuCssStyle + "\" runat=\"server\" />",
                                                 RegexOptions.Singleline)
                        + "<asp:SiteMapDataSource ID=\"DefaultVMenuDatasource\" runat=\"server\" />";
            }

            writeSourceToFile(System.IO.Path.Combine(targetPath, fileName), content);

            // Convert *.aspx.cs file
            content = readSourceFromFile(sourceFileInfo.FullName + ".cs");

            writeSourceToFile(System.IO.Path.Combine(targetPath, fileName + ".cs"), content);

            // Create *.aspx.designer.cs file
            writeSourceToFile(System.IO.Path.Combine(targetPath, fileName + ".designer.cs"), "");

            // Add files to project
            VsProjectItem aspxItem = project.AddFile(System.IO.Path.Combine(targetPath, fileName));
            project.AddFile(System.IO.Path.Combine(targetPath, fileName + ".cs"));
            project.AddFile(System.IO.Path.Combine(targetPath, fileName + ".designer.cs"));

            // Open document in designer view in order to regenerate designer file
            VsDocument document = aspxItem.OpenDesigner().Document;
            document.Save();
            document.Close();
        }

        /// <summary>
        /// Apply template to converted project folder
        /// </summary>
        /// <param name="vsProject">Visual Studio project</param>
        /// <param name="sourcePath">Location of source folder</param>
        /// <param name="targetPath">Location of target folder</param>
        public void ApplyTemplate(VsProject vsProject, string sourcePath, string targetPath)
        {
            // Process template files
            foreach (string file in Directory.GetFiles(sourcePath))
            {
                string fileName = Path.GetFileName(file);
                string targetFile = Path.Combine(targetPath, Path.GetFileName(file));

                Console.WriteLine(string.Format(Artisteer_Project_Converter.Resources.Message_CopyingTemplateFile, fileName));

                if (File.Exists(targetFile))
                {
                    copyTemplateFile(vsProject, file, targetPath);
                }
                else
                {
                    copyTemplateFile(vsProject, file, targetPath);
                    vsProject.AddFile(targetFile);
                }
            }

            // Process template sub-folders
            foreach (string subFolder in Directory.GetDirectories(sourcePath))
            {
                string folderName = Path.GetFileName(subFolder);

                if (!Directory.Exists(Path.Combine(targetPath, folderName)))
                {
                    Console.WriteLine(string.Format(Artisteer_Project_Converter.Resources.Message_CreatingFolder, folderName));

                    vsProject.addFolder(folderName);
                }

                ApplyTemplate(vsProject, subFolder, Path.Combine(targetPath, folderName));
            }
        }

        /// <summary>
        /// Copy a template file
        /// </summary>
        /// <param name="vsProject">Visual Studio project</param>
        /// <param name="sourcePath">Path to source file</param>
        /// <param name="targetPath">Path to store target file</param>
        private void copyTemplateFile(VsProject vsProject, string sourcePath, string targetPath)
        {
            FileInfo sourceFileInfo = new FileInfo(sourcePath);

            string fileName = sourceFileInfo.Name;
            string content;

            // Read template file
            using (StreamReader reader = File.OpenText(sourceFileInfo.FullName))
            {
                content = reader.ReadToEnd();
                reader.Close();
            }

            // Replace variables
            content = content.Replace("$$$PROJECT_NAME$$$", vsProject.Name);
            content = content.Replace("$$$CSS_PREFIX$$$", CssPrefix);
            content = content.Replace("$$$HMENU_CSS_STYLE$$$", HorizontalMenuCssStyle);
            content = content.Replace("$$$VMENU_CSS_STYLE$$$", VerticalMenuCssStyle);

            // Write file to target path
            using (StreamWriter writer = new StreamWriter(File.Create(Path.Combine(targetPath, fileName)), Encoding.UTF8))
            {
                writer.Write(content);
                writer.Close();
            }
        }

        /// <summary>
        /// Adjust JavaScript
        /// </summary>
        /// <param name="targetPath">Target path for Artisteer project</param>
        public void AdjustJavaScript(string targetPath)
        {
            if (Version.StartsWith("2."))
            {
                string path = Path.Combine(targetPath, "script.js");

                Console.WriteLine(string.Format(Artisteer_Project_Converter.Resources.Message_AdjustingScriptFile, path));

                string content = readSourceFromFile(path);

                content = content.Replace("artLoadEvent.add(artSetVMenusActLinks);",
                                          "// artLoadEvent.add(artSetVMenusActLinks);");

                writeSourceToFile(path, content);
            }
        }

        /// <summary>
        /// Read source from file
        /// </summary>
        /// <param name="path">Path</param>
        /// <returns>File content</returns>
        private string readSourceFromFile(string path)
        {
            string content;

            using (StreamReader reader = File.OpenText(path))
            {
                content = reader.ReadToEnd();
                reader.Close();
            }

            return content;
        }

        /// <summary>
        /// Wrtie source to file
        /// </summary>
        /// <param name="path">Path</param>
        /// <param name="content">File content</param>
        private void writeSourceToFile(string path, string content)
        {
            using (StreamWriter writer = new StreamWriter(File.Create(path), Encoding.UTF8))
            {
                writer.Write(content);
                writer.Close();
            }
        }

        /// <summary>
        /// Base path of Artisteer project
        /// </summary>
        public string BasePath
        {
            get { return basePath; }
        }

        /// <summary>
        /// CSS Prefix
        /// </summary>
        public string CssPrefix
        {
            get { return cssPrefix; }
        }

        /// <summary>
        /// CSS style for horizontal menu
        /// </summary>
        public string HorizontalMenuCssStyle
        {
            get
            {
                if (Version.StartsWith("2."))
                    return CssPrefix + "menu";
                else
                    return CssPrefix + "hmenu";
            }
        }

        /// <summary>
        /// Corresponding Artisteer version
        /// </summary>
        public string Version
        {
            get { return version; }
        }

        /// <summary>
        /// CSS style for vertical menu
        /// </summary>
        public string VerticalMenuCssStyle
        {
            get { return CssPrefix + "vmenu"; }
        }
    }
}
